/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../socket/head.h"
#include "../xos/head.h"
#include "select_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static select_impl::ITEM_DATA xos_item_data;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    select_impl::select_impl()
    {
        m_nWritesNum = 0;
        m_nReadsNum = 0;
        m_nErrorsNum = 0;
    }

    select_impl::~select_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int select_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    int select_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int select_impl::term()
    {
        int nRet = 0;

        m_nWritesNum = 0;
        m_nReadsNum = 0;
        m_nErrorsNum = 0;

        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // select function. the same as os select.
    // 
    // -1    : error
    // 0     : timeout
    // other : select num
    // 
    int select_impl::select( i_socket * pRead[], int & nReadNum,
        i_socket * pWrite[], int & nWriteNum, 
        i_socket * pError[], int & nErrorNum, 
        uint32_t uTimeout )
    {
        int nReturn = -1;
        int nRet = 0;

        if( 0 == nRet )
        {
            term();
        }

        if( 0 == nRet )
        {
            prepare_select_handles( pWrite, nWriteNum, m_pWrites, m_sWrites, m_nWritesNum );
            prepare_select_handles( pError, nErrorNum, m_pErrors, m_sErrors, m_nErrorsNum );
            prepare_select_handles( pRead, nReadNum, m_pReads, m_sReads, m_nReadsNum );
        }

        if( 0 == nRet )
        {
            nReturn = helper_select( pRead, nReadNum, pWrite, nWriteNum, pError, nErrorNum, uTimeout );
        }

        return nReturn;
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int select_impl::prepare_select_handles( i_socket * pInputHandles[], int nInputNum, 
        i_socket ** pOutputHandles, SOCKET * pOutInts, int & nOutNum )
    {
        int nRet = 0;

        for( int i = 0; i < nInputNum; ++i )
        {
            i_socket * pInputSocket = pInputHandles[i];
            pOutputHandles[nOutNum] = pInputSocket;
            pOutInts[nOutNum] = ( SOCKET )pInputSocket->get_socket();
            nOutNum++;
        }

        return nRet;
    }

    // 
    // -1    : error
    // 0     : timeout
    // other : select num
    // 
    int select_impl::helper_select( i_socket * pRead[], int & nReadNum, 
            i_socket * pWrite[], int & nWriteNum, 
            i_socket * pError[], int & nErrorNum, 
            uint32_t uTimeout )
    {
        int nReturn = -1;
        int nRet = 0;

        fd_set fd_read, fd_write, fd_error;
        fd_set * pWriteSet = 0;
        fd_set * pReadSet = 0;
        fd_set * pErrorSet = 0;
        timeval tm = { 0 };

        if( 0 == nRet )
        {
            FD_ZERO( &fd_write );
            FD_ZERO( &fd_error );
            FD_ZERO( &fd_read );
            nReadNum = 0;
            nWriteNum = 0;
            nErrorNum = 0;
        }

        // 
        // set up time
        // 
        if( 0 == nRet )
        {
            int second = uTimeout / 1000;
            int mili = uTimeout % 1000;
            tm.tv_sec = second;
            tm.tv_usec = mili * 1000;
        }

        // 
        // add read
        // 
        for( int i = 0; ( 0 == nRet ) && ( i < m_nReadsNum ); ++i )
        {
            FD_SET( m_sReads[i], &fd_read );
            pReadSet = &fd_read;
        }

        // 
        // add write
        // 
        for( int i = 0; ( 0 == nRet ) && ( i < m_nWritesNum ); ++i )
        {
            FD_SET( m_sWrites[i], &fd_write );
            pWriteSet = &fd_write;
        }

        // 
        // add error
        // 
        for( int i = 0; ( 0 == nRet ) && ( i < m_nErrorsNum ); ++i )
        {
            FD_SET( m_sErrors[i], &fd_error );
            pErrorSet = &fd_error;
        }

        if( 0 == nRet )
        {
            int nSelect = ::select( 0, pReadSet, pWriteSet, pErrorSet, &tm );

            switch( nSelect )
            {
            case SOCKET_ERROR:  // error
                {
                    nReturn = -1;
                    nRet = 1;
                }
                break;
            case 0:             // timeout
                {
                    nReturn = 0;
                    nRet = 1;
                }
                break;
            default:
                {
                    nReturn = nSelect;
                }
                break;
            }
        }

        // 
        // add read
        // 
        for( int i = 0; ( 0 == nRet ) && ( i < m_nReadsNum ); ++i )
        {
            if( FD_ISSET( m_sReads[i], &fd_read ) )
            {
                pRead[nReadNum] = m_pReads[i];
                nReadNum++;
            }
        }

        // 
        // add write
        // 
        for( int i = 0; ( 0 == nRet ) && ( i < m_nWritesNum ); ++i )
        {
            if( FD_ISSET( m_sWrites[i], &fd_write ) )
            {
                pWrite[nWriteNum] = m_pWrites[i];
                nWriteNum++;
            }
        }

        // 
        // add error
        // 
        for( int i = 0; ( 0 == nRet ) && ( i < m_nErrorsNum ); ++i )
        {
            if( FD_ISSET( m_sErrors[i], &fd_error ) )
            {
                pError[i] = m_pErrors[i];
                nErrorNum++;
            }
        }

        return nReturn;
    }

} // xos
